﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Dycox.Validation;

namespace Dycox.Workflows.Conditions
{
    public abstract class PropertyValueCondition : ValidatorBasedCondition
    {
        public PropertyValueCondition(Dictionary<string, string> parameters)
            : base()
        {
            this.PropertyName = parameters["property"];
        }

        PropertyValueValidator _validator;

        protected override Validation.Validator Validator
        {
            get
            {
                if (_validator == null)
                {
                    _validator = new PropertyValueValidator(this._expression, this.CreateInnerValidator());
                    _validator.Validating += _validator_Validating;
                }
                return _validator;
            }
        }

        void _validator_Validating(object sender, ValidationEventArgs e)
        {
            InitializeInnerValidator(((PropertyValueValidator)sender).InnerValidator, e.ValueToValidate);
        }

        protected abstract Validator CreateInnerValidator();

        protected virtual void InitializeInnerValidator(Validator validator, object valueToValidate)
        {
        }

        protected override object GetValidationValue(ConditionTestArguments args)
        {
            object container = GetValueContext(args);

            return container;// Dycox.Utils.DataBinderUtil.Eval(container, _propertyName);
        }

        protected const char ExpressionDelimiter = ':', StageNameIndicator = '#';

        private void ParsePropertyName(string propertyName)
        {
            string expression = propertyName;

            int p0 = expression.IndexOf(ExpressionDelimiter);
            string containerName;
            bool isStageName = false;

            if (p0 >= 0)
            {
                containerName = expression.Substring(0, p0);
                expression = expression.Substring(p0 + 1);
                if (containerName.Length > 1 && containerName[0] == StageNameIndicator)
                {
                    isStageName = true;
                    containerName = containerName.Substring(1);
                }
            }
            else
            {
                containerName = null;
            }

            if (string.IsNullOrWhiteSpace(expression))
                throw new ArgumentException("Invalid property name " + propertyName);

            _contextName = containerName;
            _expression = expression;
            _isStageName = isStageName;

        }

        private object GetValueContext(ConditionTestArguments args)
        {
            string containerName = _contextName;
            object container;

            if (string.IsNullOrEmpty(containerName)) containerName = "userstate";


            if (_isStageName)
            {
                IWorkflowComponent component = args.Component;

                if (component != null && !(component is WFProcess))
                {
                    Workflow wf = WorkflowManager.GetWorkflow(component);
                    WFStep step = args.GetStep();

                    if (step == null)
                        step = args.GetThread().ActiveStep;

                    if (step != null)
                    {
                        step = wf.Control.GetNearestCompletedStep(step, containerName);
                        container = wf.Stages[step].CreateContract(step.CurrentState.GetUserState());
                    }
                    else
                        container = null;

                }
                else
                    container = null;
            }
            else
            {
                switch (containerName.ToLower())
                {
                    case "userstate":
                        container = args.UseState;
                        break;
                    case "step":
                        container = args.GetStep();
                        break;
                    case "thread":
                        container = args.GetThread();
                        break;
                    case "process":
                        container = args.GetProcess();
                        break;
                    case "app":
                        container = args.ReflectionObject;
                        break;
                    default:
                        throw new NotSupportedException("Context type " + containerName + " is not supported.");
                }
            }



            return container;
        }

        private string _contextName;
        private string _expression;
        private bool _isStageName;

        //protected object GetPropertyValue(ConditionTestArguments args)
        //{
        //    object container;
        //    string expression;

        //    if (ParsePropertyName(args, out container, out expression))
        //    {
        //        object value;

        //        try { value = Dycox.Utils.DataBinderUtil.Eval(container, expression); }
        //        catch { value = null; }

        //        return value;
        //    }
        //    else
        //        return null;
        //}

        //protected Type GetPropertyType(ConditionTestArguments args)
        //{
        //    object value = GetPropertyValue(args);
        //    if (value != null) return value.GetType();
        //    else return null;
        //}

        private string _propertyName;

        public string PropertyName
        {
            get
            {
                if (_propertyName == null)
                    throw new InvalidOperationException("PropertyName property not specified.");

                return _propertyName;
            }
            private set
            {
                if (string.IsNullOrEmpty(value))
                    throw new ArgumentNullException("PropertyName");
                ParsePropertyName(value);
                _propertyName = value;
            }
        }

    }
}
